/*
 * Copyright (C) 2008 by The Regents of the University of California
 * Redistribution of this file is permitted under the terms of the GNU
 * Public License (GPL).
 *
 * @author Junghoo "John" Cho <cho AT cs.ucla.edu>
 * @date 3/24/2008
 */
 
#include "BTreeIndex.h"
#include <fstream>


using namespace std;

/*
 * BTreeIndex constructor
 */
BTreeIndex::BTreeIndex()
{
	count=0;
    rootPid = -1;
}

/*
 * Open the index file in read or write mode.
 * Under 'w' mode, the index file should be created if it does not exist.
 * @param indexname[IN] the name of the index file
 * @param mode[IN] 'r' for read, 'w' for write
 * @return error code. 0 if no error
 */
RC BTreeIndex::open(const std::string& indexname, char mode)
{
	ifstream filestr;
	int exist = 0;
	int error = 0;
	filestr.open (indexname.c_str(),ifstream::out);
	if(filestr){
		exist = 1;
		filestr.close();
	}
	else {
		exist = 0;
	}
	if(exist == 0){
		error = pf.open(indexname, mode);
		rootPid = 1; 
		treeHeight = 0;
	}
	
	if(exist == 1){
		error = pf.open(indexname, mode);
		//read pageId of root and height of tree from index file
		int buffer[2];
		pf.read(0, buffer);
		rootPid = buffer[0];
		treeHeight = buffer[1];
	}


	error = pf.open(indexname, mode);
    return error;
}

/*
 * Close the index file.
 * @return error code. 0 if no error
 */
RC BTreeIndex::close()
{
	int buffer[2] = {rootPid, treeHeight};
	pf.write(0, buffer);
    return pf.close();
}

/*
 * Insert (key, RecordId) pair to the index.ke
 * @param key[IN] the key for the value inserted into the index
 * @param rid[IN] the RecordId for the record being inserted into the index
 * @return error code. 0 if no error
 */
RC BTreeIndex::insert(int key, const RecordId& rid)
{
	locate(key, IC);
	if(LNode.insert(key, rid)==0)
	{
		LNode.write(IC.pid, pf);
	}
	else
	{
		BTLeafNode sibling;
		int siblingKey;
		LNode.insertAndSplit(key, rid, sibling, siblingKey);
		LNode.write(IC.pid, pf);
		PageId pid2=pf.endPid();
		sibling.write(pf.endPid(), pf);
		return insertIntoParent(siblingKey, IC.pid, pid2, RootToParent);
		
	}
	
}

 RC BTreeIndex::insertIntoParent(int midkey, PageId pid1, PageId pid2, stack <PageId> RtP)
 {
	if(RtP.empty())
	{
		BTNonLeafNode newRoot;
		newRoot.initializeRoot(pid1, midkey, pid2);
		RC error= newRoot.write(pf.endPid(), pf);
		treeHeight++;
		return error;
	}
	
	else
	{
		if(NLNode.read(RtP.top(), pf)==0 && NLNode.insert(midkey, RtP.top()==0))
		{
			return NLNode.write(RtP.top(), pf);
			
		}
		
		else
		{
			if(NLNode.read(RtP.top(), pf)==0)
			{
				BTNonLeafNode SiblingParent;
				NLNode.insertAndSplit(midkey, RtP.top(), SiblingParent, midkey);
				NLNode.write(RtP.top(), pf);
				PageId nextPid2=pf.endPid();
				SiblingParent.write(pf.endPid(), pf);
				PageId nextPid1=RtP.top();
				RtP.pop();
				return insertIntoParent(midkey, nextPid1, nextPid2, RtP);
			
			}
		}
	}
 }

/*
 * Find the leaf-node index entry whose key value is larger than or 
 * equal to searchKey, and output the location of the entry in IndexCursor.
 * IndexCursor is a "pointer" to a B+tree leaf-node entry consisting of
 * the PageId of the node and the SlotID of the index entry.
 * Note that, for range queries, we need to scan the B+tree leaf nodes.
 * For example, if the query is "key > 1000", we should scan the leaf
 * nodes starting with the key value 1000. For this reason,
 * it is better to return the location of the leaf node entry 
 * for a given searchKey, instead of returning the RecordId
 * associated with the searchKey directly.
 * Once the location of the index entry is identified and returned 
 * from this function, you should call readForward() to retrieve the
 * actual (key, rid) pair from the index.
 * @param key[IN] the key to find.
 * @param cursor[OUT] the cursor pointing to the first index entry
 *                    with the key value.
 * @return error code. 0 if no error.
 */
RC BTreeIndex::locate(int searchKey, IndexCursor& cursor)
{	//I changed a few things, and added a return, but I just put a return 0 in the
	//first place that made sense.
	if(treeHeight==0)
		return RC_END_OF_TREE;
	if(count==0 && treeHeight > 1)
	{
		NLNode.read(rootPid, pf);
		RootToParent.push(rootPid);
	}
	else if (count>0 && count < treeHeight)
	{
		NLNode.read(cursor.pid, pf);
		RootToParent.push(cursor.pid);
	}
	else 
	{
		if(treeHeight == 1){
			LNode.read(rootPid,pf);
		}
		else{
			LNode.read(cursor.pid, pf);
		}
	}
    if(count<treeHeight)
	{
		NLNode.locateChildPtr(searchKey, cursor.pid); 
		count++;
		locate(searchKey, cursor);
	}	
	else
	{
		LNode.locate(searchKey, cursor.eid);
		count=0;
		return 0;
	}
}

/*
 * Read the (key, rid) pair at the location specified by the index cursor,
 * and move foward the cursor to the next entry.
 * @param cursor[IN/OUT] the cursor pointing to an leaf-node index entry in the b+tree
 * @param key[OUT] the key stored at the index cursor location.
 * @param rid[OUT] the RecordId stored at the index cursor location.
 * @return error code. 0 if no error
 */
RC BTreeIndex::readForward(IndexCursor& cursor, int& key, RecordId& rid)
{
	//not sure if node is leaf or not. I think so.
	int error = 0;
	LNode.read(cursor.pid, pf);
	key = LNode.keys[cursor.eid];
	rid = LNode.rids[cursor.eid];
	if(cursor.eid < 0 || cursor.eid > LNode.ENTRIES_PER_NODE || cursor.pid < 0){
		error = RC_INVALID_CURSOR;
	}
	if(cursor.eid == LNode.ENTRIES_PER_NODE){
		cursor.pid = LNode.nextPage;
		cursor.eid = 0;
	}
	else{
		cursor.eid++;
	}
    return error;
}
